﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Timers;
using System.Windows.Forms;

namespace Snake1._0
{
    /// <summary>
    /// 方向枚举
    /// </summary>
    public enum Direction
    {
        Left,
        Right,
        Up,
        Down
    }
    class Palette
    {
        private int _with = 20;//宽度
        private int _height = 20;//高度
        private Color _bgColor;//背景颜色
        private Graphics _gpPalette;//画布
        public Control _labelCount =null;//分数控件
        public Control _labelLevel = null;//级别控件

        private ArrayList _snakes;//蛇块列表
        private Direction _direction;//前进方向
        private System.Timers.Timer timerBlock;//更新器
        private Snake _food;//当前食物
        private int _size = 20;//单位大小
        private int _level = 1;//游戏等级
        private bool _isstop = false;//是否暂停(true表示暂停)
        private bool _isGameOver = false;//表示游戏是否结束
        //表示蛇移动的速度
        private int[] _speed = new int[] { 500,450,400,350,300,250,200,150,100,50};
        //升级需要的蛇块长度
        private int[] _levelexp = new int[] { 5, 10, 12, 13, 14, 15, 16, 17, 18, 19 };

        public Direction Direction
        {
            get { return _direction; }
            set { this._direction = value; }
        }
        public bool IsStop
        {
            get { return _isstop; }
            
        }
        public bool IsGameOver
        {
            get { return _isGameOver; }
        }

        public Palette(int width, int height, int size, Color bgColor, Graphics g, int level,Control labelcount, Control labelevel)
        {
            this._with = width;
            this._height = height;
            this._bgColor = bgColor;
            this._gpPalette = g;
            this._size = size;
            this._level = level-1;
            //初始化蛇块列表
            this._snakes = new ArrayList();
            //生成一个蛇头
            this._snakes.Insert(0, (new Snake(Color.Red, this._size, new Point(width / 2, height / 2))));
            this._direction = Direction.Right;

            _labelCount = labelcount;
            _labelLevel = labelevel;
            _labelCount.Text = "1";
            _labelLevel.Text = level.ToString();
        }

        /// <summary>
        /// 开始游戏
        /// </summary>
        public void Start()
        {
            if(this._isstop)
            {
                //如果是暂停就启用
                this._isstop = false;
                this.timerBlock.Start();
            }
            else
            {
                this._food = GetFood();//生成一个食物
                timerBlock = new System.Timers.Timer(_speed[this._level]);
                timerBlock.Elapsed += new ElapsedEventHandler(OnBlockTimedEvent);
                timerBlock.AutoReset = true;
                timerBlock.Start();
            }
            
        }

        /// <summary>
        /// 暂停游戏
        /// </summary>
        public void Stop()
        {
            this._isstop = true;
            this.timerBlock.Stop(); 
        }

        /// <summary>
        /// 定时更新
        /// </summary>
        /// <param name="source"></param>
        /// <param name="e"></param>
        private void OnBlockTimedEvent(object source,ElapsedEventArgs e)
        {
            this.Move();//前进一个单位
            if(this.CheckDead())//检测是否游戏结束
            {
                this.timerBlock.Stop();
                this.timerBlock.Dispose();
                System.Windows.Forms.MessageBox.Show("Score:"+this._snakes.Count,"Game Over");
            }
            else
            {
                //长度达到20 游戏结束
                if (this._snakes.Count == 30)
                {
                    this._isGameOver = true;
                    this.timerBlock.Stop();
                    this.timerBlock.Dispose();
                    System.Windows.Forms.MessageBox.Show("恭喜通关成功！", "Game Over");
                }
            }
        }

        /// <summary>
        /// 检测是否结束(一种是撞上边界,即蛇头坐标超出画布范围,一种是蛇头撞上蛇身,也就是蛇头的坐标和蛇身的某个坐标重合)
        /// </summary>
        /// <returns></returns>
        public bool CheckDead()
        {
            bool state = false;
            Snake head = (Snake)(this._snakes[0]);//取蛇块列表第一个,即蛇头
            //检测是否超出画布范围
            if(head.Point.X<0||head.Point.Y<0||head.Point.X>=this._with||head.Point.Y>=this._height)
            {
                state= true;
            }
            //检测是否撞上自己
            for(int i=1; i<this._snakes.Count;i++)
            {
                Snake s = (Snake)this._snakes[i];
                if(head.Point.X==s.Point.X&&head.Point.Y==s.Point.Y)
                {
                    state= true;
                    break;
                }
            }
            this._isGameOver = state;
            return state;
        }

        /// <summary>
        /// 移动
        /// </summary>
        private void Move()
        {
            Point p;//下一个坐标位置
            Snake head = (Snake)(this._snakes[0]);//蛇头
            if(this._direction==Direction.Up)
            {
                p = new Point(head.Point.X,head.Point.Y-1);
            }
            else if (this._direction == Direction.Down)
            {
                p = new Point(head.Point.X, head.Point.Y + 1);
            }
            else if (this._direction == Direction.Left)
            {
                p = new Point(head.Point.X-1, head.Point.Y);
            }
            else 
            {
                p = new Point(head.Point.X+1, head.Point.Y);
            }
            //生成新蛇头
            Snake s = new Snake(Color.Red, this._size, p);

            //把下一个坐标插入蛇块列表的第一个,即其成为蛇头
            this._snakes.Insert(0, s);

            //如果下一个坐标不是食物坐标,那么从蛇块列表中删除最后一个蛇块
            if (this._food.Point!=p)
            {
                this._snakes.RemoveAt(this._snakes.Count-1);
            }
            else
            {
                //如果下一个坐标和食物重合,那么生成一个新的食物
                this._food = this.GetFood();

                //判断是否升级速度(每增加2个就升1级)
                if (this._snakes.Count  == this._levelexp[this._level] && this._level+1 != _speed.Length)
                {
                    this._level = this._level + 1;
                    this.timerBlock.Interval = this._speed[this._level];//要从下标0开始

                    // 当一个控件的InvokeRequired属性值为真时，说明有一个创建它以外的线程想访问它
                    Action<string> actionDelegate_level = (x) => { this._labelLevel.Text = x.ToString(); };
                    // 或者
                    // Action<string> actionDelegate = delegate(string txt) { this._labelLevel.Text = txt; };
                    this._labelCount.BeginInvoke(actionDelegate_level, this._level.ToString());
                }
                //跨线程访问控件 http://www.cnblogs.com/TankXiao/p/3348292.html
                // 由于这个方法是用timer创建的所以InvokeRequired的永远为真，即不需要if判断，说明有一个创建它以外的线程想访问它
                Action<string> actionDelegate_count = (x) => { this._labelCount.Text = x.ToString(); };
                // 或者
                // Action<string> actionDelegate = delegate(string txt) { this._labelCount.Text = txt; };
                this._labelCount.BeginInvoke(actionDelegate_count, this._snakes.Count.ToString());

            }
            
            //更新画布
            this.PaintPalette(this._gpPalette);
        }

        /// <summary>
        /// 生成下一个食物
        /// </summary>
        /// <returns></returns>
        private Snake GetFood()
        {
            Snake food = null;
            Random r = new Random();
            bool redo = true;
            while(redo)
            {
                redo = false;
                int x = r.Next(this._with);
                int y = r.Next(this._height);
                //循环检测食物是否和蛇块列表有冲突
                for(int i=0; i<this._snakes.Count;i++)
                {
                    Snake s = (Snake)(this._snakes[i]);
                    if(s.Point.X==x&&s.Point.Y==y)
                    {
                        //有冲突时再随机找一个坐标
                        redo = true;
                    }
                }
                if(redo==false)
                {
                    food = new Snake(Color.Black, this._size, new Point(x, y));
                }
            }
            return food;
        }

        /// <summary>
        /// 更新画布
        /// </summary>
        /// <param name="gp"></param>
        public void PaintPalette(Graphics gp)
        {
            
            gp.Clear(this._bgColor);
            this._food.Paint(gp);
            foreach(Snake s in this._snakes)
            {
                s.Paint(gp);
            }
        }

    }

    
}
